#!/usr/bin/env node
// 配置运行node的位置
const path = require('path')
const { Readable } = require('stream')
// 导入所需的koa模块
const Koa = require('koa')
const send = require('koa-send')
const compilerSFC = require('@vue/compiler-sfc')

// 创建 实例 koa 
const app = new Koa()

// 流转换字符串函数
// 因为转换流是一个异步操作，所以我们需要返回一个 Promise 对象
const stremToString = stream => new Promise((resolve, reject) => {
  const chunks = []
  // 注册 stream 的 data 事件， 去监听读取到的 buffer
  stream.on('data', chunk => chunks.push(chunk))
  // 数据读取完毕，调用 resolve 并传递数据
  stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')))
  // 注册错误事件
  stream.on('error', reject)
})
const stringToStream = text => {
  const stream = new Readable()
  stream.push(text)
  stream.push(null) // 标记结束
  return stream
}


// 3. 加载第三方模块
app.use(async (ctx, next) => {
  if(ctx.path.startsWith('/@modules/')) {
    // 拿到模块的名称
    const moduleName = ctx.path.substring(10)
    // package.json 文件的路径
    const pkgPath = path.join(process.cwd(), 'node_modules', moduleName, 'package.json')
    const pkg = require(pkgPath)
    // 为请求路径进行赋值
    ctx.path = path.join('/node_modules',moduleName, pkg.module)
  }
  await next()
})


// 1. 静态文件服务器
// 使用Koa开发静态web服务器，默认返回根目录下的 index.html
//  首先创建一个中间件，默认加载当前目录下，也就是运行该命令行目录中的 index.html
/**
 * @param ctx 当前上下文
 */
app.use(async (ctx, next) => {
  /**
   * @param ctx 当前上下文
   * @param ctx.path 请求的路径
   * @param 根目录
   */
  await send(ctx, ctx.path, { root: process.cwd(), index: 'index.html' })
  // 接下来 让他运行其他的中间件
  await next()
})



// 4.处理单文件组件
app.use(async (ctx, next) => {
  if(ctx.path.endsWith('.vue')) {
    // 需要转换流
    const contents = await stremToString(ctx.body)
    const { descriptor } = compilerSFC.parse(contents)
    let code
    // 如果没有type请求，那就是第一次请求
    if(!ctx.query.type) {
      code = descriptor.script.content
      code = code.replace(/export\s+default\s+/g, 'const __script = ')
      code += `
      import { render as __render } from "${ctx.path}?type=template"
      __script.render = __render
      export default __script
      `
    } else if (ctx.query.type === 'template') {
      const templateRender = compilerSFC.compileTemplate({ source: descriptor.template.content })
      code = templateRender.code
    }
    ctx.type = 'application/javascript'
    ctx.body = stringToStream(code)
  }
  await next()
})


// 2. 当把静态文件返回给浏览器之前判断是否是**js模块**，如果是的话就修改模块路径为`/@/modules/${模块名称}`
app.use(async (ctx, next) => {
  if(ctx.type === 'application/javascript') {
    const contents = await stremToString(ctx.body)
    // import vue from 'vue'
    // import App from './App.vue'
    ctx.body = contents
      .replace(/(from\s+['"])(?![\.\/])/g, '$1/@modules/')
      .replace(/process\.env\.NODE_ENV/g, '"development"')
  }
})

app.listen(3000)
console.log('Asever running @http://localhost:3000')